﻿using CommonEngine.STSDB.Database;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace CommonEngine.STSDB.Core
{
    public class NoSQLDB : INoSQLDB
    {
        private const string _NAME = "STSDB.stsdb4";
        private string FILE_NAME = "STSDB.stsdb4";

        /// <summary>
        /// 限制构造
        /// </summary>
        internal NoSQLDB()
        {
            //注意，不要将数据包放在bin目录，iis在监听文件变动时，会刷新缓存
            //放在bin下，会导致缓存重置
            this.FILE_NAME = STSFactory.STSPath() + _NAME;
        }

        public bool Exists<T>(T model) where T : class, IEn
        {
            if (model == null)
                return false;

            using (IStorageEngine engine = STSdb.FromFile(FILE_NAME))
            {
                Type t = typeof(T);
                ITable<Guid, T> table = engine.OpenXTable<Guid, T>(t.Name);

                return table.Exists(model.Id);
            }
        }

        public bool Insert<T>(T model) where T : class, IEn
        {
            if (model == null)
                return false;

            using (IStorageEngine engine = STSdb.FromFile(FILE_NAME))
            {
                Type t = typeof(T);
                ITable<Guid, T> table = engine.OpenXTable<Guid, T>(t.Name);

                table[model.Id] = model;
                engine.Commit();
            }

            return true;
        }

        public bool Insert<T>(List<T> models) where T : class, IEn
        {
            if (models == null)
                return false;

            if (models.Count < 1)
                return false;

            using (IStorageEngine engine = STSdb.FromFile(FILE_NAME))
            {
                Type t = typeof(T);
                ITable<Guid, T> table = engine.OpenXTable<Guid, T>(t.Name);

                foreach (var kv in models)
                {
                    if (kv != null)
                        table[kv.Id] = kv;
                }

                engine.Commit();
            }

            return true;
        }

        public bool Update<T>(T model) where T : class, IEn
        {
            if (model == null)
                return false;

            using (IStorageEngine engine = STSdb.FromFile(FILE_NAME))
            {
                Type t = typeof(T);
                ITable<Guid, T> table = engine.OpenXTable<Guid, T>(t.Name);

                table.Replace(model.Id, model);
                engine.Commit();
            }

            return true;
        }

        public bool Update<T>(List<T> models) where T : class, IEn
        {
            if (models == null)
                return false;

            if (models.Count < 1)
                return false;

            using (IStorageEngine engine = STSdb.FromFile(FILE_NAME))
            {
                Type t = typeof(T);
                ITable<Guid, T> table = engine.OpenXTable<Guid, T>(t.Name);

                foreach (var kv in models)
                {
                    if (kv != null)
                        table.Replace(kv.Id, kv);
                }

                engine.Commit();
            }

            return true;
        }

        public bool Delete<T>(Guid ID) where T : class, IEn
        {
            using (IStorageEngine engine = STSdb.FromFile(FILE_NAME))
            {
                Type t = typeof(T);
                ITable<Guid, T> table = engine.OpenXTable<Guid, T>(t.Name);

                table.Delete(ID);
                engine.Commit();
            }
            return true;
        }

        public bool Delete<T>(T model) where T : class, IEn
        {
            if (model == null)
                return false;

            using (IStorageEngine engine = STSdb.FromFile(FILE_NAME))
            {
                Type t = typeof(T);
                ITable<Guid, T> table = engine.OpenXTable<Guid, T>(t.Name);

                table.Delete(model.Id);
                engine.Commit();
            }
            return true;
        }

        public bool Delete<T>(List<T> models) where T : class, IEn
        {
            if (models == null)
                return false;

            if (models.Count < 1)
                return false;

            using (IStorageEngine engine = STSdb.FromFile(FILE_NAME))
            {
                Type t = typeof(T);
                ITable<Guid, T> table = engine.OpenXTable<Guid, T>(t.Name);

                foreach (var kv in models)
                {
                    if (kv != null)
                        table.Delete(kv.Id);
                }

                engine.Commit();
            }

            return true;
        }

        public bool Delete<T>(Expression<Func<T, bool>> express) where T : class, IEn
        {
            if (express == null)
                return this.Clear<T>();

            List<T> Tls = this.GetMore<T>(express);

            return this.Delete<T>(Tls);
        }

        public bool Clear<T>() where T : class, IEn
        {
            using (IStorageEngine engine = STSdb.FromFile(FILE_NAME))
            {
                Type t = typeof(T);
                ITable<Guid, T> table = engine.OpenXTable<Guid, T>(t.Name);

                table.Clear();
                engine.Commit();
            }
            return true;
        }

        public T GetOne<T>(Guid ID) where T : class, IEn
        {
            using (IStorageEngine engine = STSdb.FromFile(FILE_NAME))
            {
                Type t = typeof(T);
                ITable<Guid, T> table = engine.OpenXTable<Guid, T>(t.Name);
                return table.TryGetOrDefault(ID, default(T));
            }
        }

        public T GetOne<T>(Expression<Func<T, bool>> express) where T : class, IEn
        {
            List<T> Tls = new List<T>();
            using (IStorageEngine engine = STSdb.FromFile(FILE_NAME))
            {
                Type t = typeof(T);
                ITable<Guid, T> table = engine.OpenXTable<Guid, T>(t.Name);
                if (table.Count() > 0)
                    Tls = new List<T>(table.Forward().Select(a => a.Value));
            }

            if (express == null)
                return Tls.FirstOrDefault();

            return Tls.FirstOrDefault(express.Compile());
        }

        public List<T> GetMore<T>(Expression<Func<T, bool>> express) where T : class, IEn
        {
            List<T> Tls = new List<T>();
            using (IStorageEngine engine = STSdb.FromFile(FILE_NAME))
            {
                Type t = typeof(T);
                ITable<Guid, T> table = engine.OpenXTable<Guid, T>(t.Name);
                if (table.Count() > 0)
                    Tls = new List<T>(table.Forward().Select(a => a.Value));
            }

            if (express == null)
                return Tls;

            return Tls.Where(express.Compile()).ToList();
        }

        public List<T> GetMore<T>(Expression<Func<T, bool>> express, Expression<Func<T, object>> OrderExpress, bool IsASC = true) where T : class, IEn
        {
            List<T> Tls = new List<T>();
            using (IStorageEngine engine = STSdb.FromFile(FILE_NAME))
            {
                Type t = typeof(T);
                ITable<Guid, T> table = engine.OpenXTable<Guid, T>(t.Name);
                if (table.Count() > 0)
                    Tls = new List<T>(table.Forward().Select(a => a.Value));
            }

            if (express == null)
            {
                if (OrderExpress == null)
                {
                    return Tls;
                }
                else
                {
                    if (IsASC)
                    {
                        return Tls.OrderBy(OrderExpress.Compile()).ToList();
                    }
                    else
                    {
                        return Tls.OrderByDescending(OrderExpress.Compile()).ToList();
                    }
                }
            }

            if (OrderExpress != null)
            {
                if (IsASC)
                {
                    return Tls.Where(express.Compile()).OrderBy(OrderExpress.Compile()).ToList();
                }
                else
                {
                    return Tls.Where(express.Compile()).OrderByDescending(OrderExpress.Compile()).ToList();
                }
            }

            return Tls.Where(express.Compile()).ToList();
        }

        public int Count<T>(Expression<Func<T, bool>> express = null) where T : class, IEn
        {
            if (express == null)
            {
                using (IStorageEngine engine = STSdb.FromFile(FILE_NAME))
                {
                    Type t = typeof(T);
                    ITable<Guid, T> table = engine.OpenXTable<Guid, T>(t.Name);

                    return (int)table.Count();
                }
            }
            else
            {
                List<T> Tls = new List<T>();
                using (IStorageEngine engine = STSdb.FromFile(FILE_NAME))
                {
                    Type t = typeof(T);
                    ITable<Guid, T> table = engine.OpenXTable<Guid, T>(t.Name);
                    if (table.Count() > 0)
                        Tls = new List<T>(table.Forward().Select(a => a.Value));
                }
                return Tls.Count(express.Compile());
            }
        }

        public List<T> PageByExp<T>(Expression<Func<T, bool>> express, Expression<Func<T, object>> By, bool IsAsc, int pageIndex, int pageSize, out int totalRecord) where T : class, IEn
        {

            var LS = this.GetMore<T>(express, By, IsAsc);
            return this.LinqByPage<T>(LS, pageIndex, pageSize, out totalRecord);

        }

        private List<T> LinqByPage<T>(List<T> ALLData, int pageIndex, int pageSize, out int totalCount) where T : class, IEn
        {
            totalCount = 0;
            if (ALLData == null)
                return new List<T>();

            totalCount = ALLData.Count;

            int MaxPage = 1 + (int)(totalCount / (pageSize <= 0 ? 20 : pageSize));
            if (pageIndex < 1)
                pageIndex = 1;
            else if (pageIndex > MaxPage)
                pageIndex = MaxPage;

            return ALLData.Count > 0 ? ALLData.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList() : new List<T>();
        }
    }
}
